/*
Automatic clinic tests using a robot arm.
Copyright (C) 2010  CIC-CPC, CHU Gabriel Montpied
All other trademarks, logos and copyrights are the property
of their respective owners.

This file is part of cpc-painbot.

cpc-painbot is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

cpc-painbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with cpc-painbot.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef CPC_PAINBOT_BRAS_H
#define CPC_PAINBOT_BRAS_H
#include "pilotableserie.h"
#include "positionbras.h"

class QTimer;

/*! Contrôle du bras robot.
    Le Bras robot se commande au travers d'une liaison série.
    Lorsqu'il reçoit une Commande, il retransmet systématiquement une Commande
    qui est le pendant de celle envoyée. Ce tandem est retranscrit dans cette
    classe grâce au signal de la classe mère.
*/
class Bras : public PilotableSerie
{
Q_OBJECT
//! Indique si le bras est présent ou non (physiquement parlant).
Q_PROPERTY(bool present READ isPresent NOTIFY estPresent)
//! Indique si le bras a été initialisé ou non.
Q_PROPERTY(bool init READ isInit NOTIFY estInitialise)
//! Bras prêt Ã  recevoir des commandes ou non.
Q_PROPERTY(bool pret READ isPret NOTIFY estPret)
//! Etat courant du bras.
Q_PROPERTY(Etat etat READ etat NOTIFY etatChange)
//! Position courante du bras.
Q_PROPERTY(PositionBras position READ position)
//! Etats possibles du bras.
Q_ENUMS(Etat)
public:
    //! Etats du bras.
    enum Etat {
        EtatDeconnecte,        //!< Le bras n'est pas relié.
        EtatNonPret,           //!< Le bras est connecté mais pas initialisé.
        EtatPret,              //!< Le bras est prêt, en attente d'une commande.
        EtatInit,              //!< En cours d'initialisation.
        EtatDeplacement,       //!< En cours de déplacement.
        EtatLecturePosition    //!< Lecture de la position.
    };

    /*! Constructeur.
        Initialise la liaison série et vérifie la présence du robot.

        \param[in] port Nom du port série sur lequel le robot est branché.
    */
    Bras(QObject *parent = 0);

    /*! Destructeur.
        S'assure que le Bras soit dans un état correct.
    */
    ~Bras();

    /*! Indique si le bras est présent.
        Si le Bras répond à un Ping, c'est qu'il est présent physiquement.
        \retval true Le Bras est présent.
        \retval false Le Bras est absent \em ou ce n'est pas le Bras qui est connecté.
    */
    bool isPresent() const { return present_; }

    /*! Indique si le bras est initialisé.
        Le bras a besoin d'être initialisé mécaniquement avant
        de pouvoir être utilisé.

        \retval true    Le bras est initialisé.
        \retval false   Le bras n'est pas initialisé.
    */
    bool isInit() const { return init_; }

    /*! Bras prêt à recevoir des Commandes.
        \retval true Le Bras est prêt.
        \retval false Le Bras n'est pas prêt.
    */
    bool isPret() const { return pret_; }

    /*! Retourne l'état courant du bras.
        \return L'état du bras.
    */
    Etat etat() const { return etat_; }

    /*! Position courante du bras.
        \return La position du bras.
    */
    PositionBras position() const { return pos_; }


public slots:
    /*! Test de présence.
        Envoie une Commande de 'ping' pour s'assurer que le robot est au bout
        du fil.
    */
    void ping();

    /*! Initialisation.
        Initialise le robot mécaniquement.
    */
    void initialiser();

    /*! Arrête le Bras.
        Stop toute action du bras.
    */
    void arreter();

    /*! Lecture de la position.
        Demande au bras de fournir sa position courante.
    */
    void lirePosition();

    /*! Effectue un déplacement.
        Envoie un ordre de déplacement pour attendre la position
        indiquée.

        \param[in] pos La position à atteindre
    */
    void deplacer(const PositionBras &pos);

    /*! Effectue un déplacement.
        Se place à la position indiquée à la vitesse indiquée.

        \param[in] pos La position à atteindre
        \param[in] vitesse La vitesse du déplacement
    */
    void deplacer(const PositionBras &pos, float vitesse);

    /*! Effectue un déplacement.
        Envoie un ordre de déplacement au point indiqué.

        \param[in] dx déplacement en X
        \param[in] dy déplacement en Y
        \param[in] dz déplacement en Z
        \param[in] dr déplacement en R
        \param[in] vitesse vitesse du déplacement
    */
    void deplacer(float dx, float dy, float dz, float dr, float vitesse);

    /*! Rotation de la tête.
        \param[in] angle Angle que la tête doit atteindre
    */
    void rotationTete(float angle);

private slots:
    //! Change l'état de présence du Bras.
    void setPresent(bool present = false);
    /*! Change le fait que le bras soit initialisé. */
    void setInit(bool init = false) { init_ = init; emit estInitialise(init_); }
    /*! Change le fait que le bras soit prêt. */
    void setPret(bool pret = false);
    //! Définit la position.
    void setPosition(const PositionBras &pos) { pos_ = pos; emit positionChangee(pos_); }
    //! Change l'état.
    void setEtat(Etat etat) { etat_ = etat; emit etatChange(etat_); }

    virtual bool hasCommande() const;
    virtual void parserCommande();

signals:
    //! Indique la présence/l'absence du bras au bout du câble.
    void estPresent(bool etat);
    //! Le bras a changé de position.
    void positionChangee(const PositionBras &pos);
    //! Le bras est à présent initialisé.
    void estInitialise(bool init);
    //! Le bras est prêt.
    void estPret(bool pret);
    void erreurTransmission();
    void etatChange(Etat);
    void erreur(int type);

protected:
    /*! Configure la liaison série.
        Configure les éléments propres à la communication avec le bras :
        \li Vitesse à 38400 bauds.
    */
    virtual void configurerLiaison();
    bool present_; //!< Bras présent/absent au bout du lien.
    bool init_; //!< Le bras a été initialisé.
    bool pret_; //!< Le bras est prêt.
    Etat etat_; //!< Etat courant du bras.
    PositionBras pos_; //!< Position courante.
    QTimer *delai_; //!< Attente d'une réponse.

private:
    /*! Changement d'état.
        Contrôle la logique des transitions d'état du bras.
    */
    void changerEtatPing(const QSharedPointer<commande::Commande> &cmd);
    void changerEtatReponse(const QSharedPointer<commande::Commande> &cmd);
};
#endif // CPC_PAINBOT_BRAS_H
